package com.kantboot.security.service.impl;

import com.kantboot.security.domain.entity.SecurityRequest;
import com.kantboot.security.dto.SecurityRequestDTO;
import com.kantboot.security.repositroy.SecurityRequestRepository;
import com.kantboot.security.service.ISecurityRequestService;
import com.kantboot.security.util.SecurityRequestUtil;
import com.kantboot.util.common.aes.AesUtil;
import com.kantboot.util.common.exception.BaseException;
import com.kantboot.util.common.http.HttpRequestHeaderUtil;
import com.kantboot.util.common.rsa.RSAUtil;
import com.kantboot.util.core.application.PortGetter;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Response;
import org.springframework.stereotype.Service;

import java.util.Enumeration;
import java.util.concurrent.TimeUnit;

/**
 * 安全请求服务实现
 *
 * @author 方某方
 */
@Slf4j
@Service
public class SecurityRequestServiceImpl implements ISecurityRequestService {

    @Resource
    private SecurityRequestRepository repository;

    @Resource
    private HttpRequestHeaderUtil httpRequestHeaderUtil;

    /**
     * 获取项目内访问的前缀的一个工具
     */
    @Resource
    private PortGetter portGetter;

    @Resource
    private HttpServletRequest request;

    @Override
    public SecurityRequest generate() {
        SecurityRequest securityRequest = new SecurityRequest();
        // 给SecurityRequest生成公钥和私钥
        SecurityRequestUtil.generateKey(securityRequest);
        // 根据SecurityRequest生成常规信息
        // 生成ip、User-agent、token、过期时间等信息
        SecurityRequestUtil.generateCommonInfo(securityRequest, httpRequestHeaderUtil);
        // 保存进数据库
        return repository.save(securityRequest);
    }

    @Override
    public String getNewPublicKey() {
        // 生成并返回新的公钥
        return generate().getPublicKey();
    }

    /**
     * 获取项目内访问的前缀
     */
    private String getPrefix(String uri) {
        // 获取当前项目的端口
        int port = portGetter.getPort();
        // 前缀，即回环地址加端口
        String prefix = "http://127.0.0.1:" + port;
        // 如果uri不是以/开头，则加上/
        if (!uri.startsWith("/")) {
            uri = "/" + uri;
        }
        return prefix;
    }

    @Override
    public String forward(SecurityRequestDTO dto) {
        // 根据publicKey获取对应的安全请求实体
        SecurityRequest securityRequest = repository.findByPublicKey(dto.getPublicKey());
        // 校验实体
        SecurityRequestUtil.checkBeforeForward(dto, securityRequest, httpRequestHeaderUtil);

        // 补充DTO，使其完整，包含body、uri、method等信息
        SecurityRequestUtil.supplementDTO(dto);

        // 获取对应私钥
        String privateKey = repository.findByPublicKey(dto.getPublicKey()).getPrivateKey();
        // 解密body
        String body = SecurityRequestUtil.decryptBody(dto.getBody(), privateKey);


        String uri = dto.getUri();
        String prefix = getPrefix(uri);
        // 根据uri和method转发请求
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(10, TimeUnit.SECONDS) // 连接超时
                .readTimeout(600, TimeUnit.SECONDS) // 读取超时
                .build();
        // 请求构建器
        okhttp3.Request.Builder builder = new okhttp3.Request.Builder();
        // 请求构建器设置url
        builder.url(prefix + uri);

        // 请求构建器设置方法
        builder.method(dto.getMethod(), okhttp3.RequestBody.create(body, okhttp3.MediaType.parse("application/json")));
        // 获取request中的header
        Enumeration<String> headerNames = request.getHeaderNames();
        // 遍历header
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            // 设置header
            builder.addHeader(headerName, request.getHeader(headerName));
        }

        // 发送json数据
        builder.addHeader("Content-Type", "application/json");
        // 设置ip
        builder.addHeader("x-forwarded-for", httpRequestHeaderUtil.getIp());
        // 请求构建器构建请求
        okhttp3.Request request = builder.build();
        // 请求执行器执行请求
        try {
            Response execute = client.newCall(request).execute();

            String string = execute.body().string();

            // 解密clientKeyInRsa
            String clientKeyInRsa = dto.getClientKeyInRsa();
            String clientKey = RSAUtil.decryptByPrivateKey(clientKeyInRsa, securityRequest.getPrivateKey());
            // 加密返回请求结果
            return AesUtil.encryptData(string, clientKey);
        } catch (Exception e) {
            // 告知客户端，加密失败
            throw BaseException.of("encryptError", "加密失败");
        }
    }

    @Override
    public String simpleForward(String content) {
        // 获取全部uri
        String requestURI = request.getRequestURI();
        // 获取simpleForward后的uri
        String uri = requestURI.substring(requestURI.indexOf("/simpleForward/")+"/simpleForward/".length()-1);
        // 默认所有请求都是post请求
        String method = "POST";
        // 分为三个.分割，第一个是后端生成的公钥，第二个前端加密后的请求体，第三个是客户端生成的公钥
        String[] split = content.split("\\.");

        return forward(new SecurityRequestDTO()
                .setUri(uri)
                .setPublicKey(split[0])
                .setBody(split[1])
                .setClientKeyInRsa(split[2])
        );
    }

}